package org.ssssssss.curd.generator;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpMethod;
import org.ssssssss.curd.exception.MagicApiCurdGenerateException;
import org.ssssssss.curd.pojo.TableColumn;
import org.ssssssss.curd.strategy.RouteGetTableMetaStrategy;
import org.ssssssss.curd.util.AssertUtil;
import org.ssssssss.curd.util.MagicApiCurdSpringUtil;
import org.ssssssss.magicapi.core.config.MagicConfiguration;
import org.ssssssss.magicapi.core.model.*;
import org.ssssssss.magicapi.datasource.model.DataSourceInfo;
import org.ssssssss.magicapi.datasource.model.MagicDynamicDataSource;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Magic-api CURD API 生成器
 */
public class MagicApiCurdGenerator implements InitializingBean {

    private static final Logger log = LoggerFactory.getLogger(MagicApiCurdGenerator.class);

    /**
     * 分页 API 脚本模板
     */
    private final static String PAGE_SCRIPT_TEMPLATE ="page-list-script.template";
    /**
     * 保存 API 脚本模板
     */
    private final static String SAVE_SCRIPT_TEMPLATE="save-script.template";
    /**
     * 删除 API 脚本模板
     */
    private final static String DELETE_SCRIPT_TEMPLATE="delete-script.template";
    /**
     * 正则表达式：匹配下划线
     */
    private final static Pattern LINE_PATTERN = Pattern.compile("_(\\w)");

    /**
     * Magic-api 数据源对象
     */
    private DataSourceInfo dataSourceInfo;
    /**
     * 要生成 CURD API 的表名
     */
    private String tableName;
    /**
     * {@link #tableName} 的所有字段元信息
     */
    private List<TableColumn> tableColumns;
    /**
     * Magic-api 分租ID
     */
    private String groupId;

    /**
     * 新建一个 {@link MagicApiCurdGenerator} 对象
     * @param dataSourceInfo  Magic-api 数据源对象
     * @param tableName 要生成 CURD API 的表名
     */
    public MagicApiCurdGenerator(DataSourceInfo dataSourceInfo, String tableName) {
        this.dataSourceInfo = dataSourceInfo;
        this.tableName=tableName;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //根据数据源初始化 tableColumns
        RouteGetTableMetaStrategy routeGetTableMetaStrategy= MagicApiCurdSpringUtil.getBean(RouteGetTableMetaStrategy.class, dataSourceInfo,
                MagicApiCurdSpringUtil.getBean(MagicDynamicDataSource.class));
        tableColumns = routeGetTableMetaStrategy.listTableColumns(tableName);
    }

    /**
     * 生成 CURD API
     */
    public void generator(){
        //生成 分组
        generateGroupApi();
        //生成 分页API
        generatePageApi();
        //生成 保存API
        generateSaveApi();
        //生成 删除API
        generateDeleteApi();
    }

    /**
     * 生成 分组
     */
    private void generateGroupApi(){
        Group group=new Group();
        group.setType("api");
        group.setParentId("0");
        group.setName(tableName);
        group.setPath("/"+tableName);
        //保存
        AssertUtil.isTrue(MagicConfiguration.getMagicResourceService().saveGroup(group),
                new MagicApiCurdGenerateException(String.format("save group.name=%s  fail",group.getName())));
        groupId=group.getId();
    }


    /**
     * 生成 分页API
     */
    private void generatePageApi(){
        ApiInfo apiInfo=new ApiInfo();
        apiInfo.setPath("/page");
        apiInfo.setName("page-"+tableName);
        apiInfo.setGroupId(groupId);
        apiInfo.setMethod(HttpMethod.POST.name());
        //requestBodyDefinition
        apiInfo.setRequestBodyDefinition(buildBaseDefinition());
        //requestBody
        apiInfo.setRequestBody(buildRequestBodyJson().toJSONString());
        //script
        apiInfo.setScript(generatePageScript());
        //保存
        AssertUtil.isTrue(MagicConfiguration.getMagicResourceService().saveFile(apiInfo),
                new MagicApiCurdGenerateException(String.format("save page api fail,apiInfo.name = %s",apiInfo.getName())));
    }

    /**
     * 生成 保存API
     */
    private void generateSaveApi(){
        ApiInfo apiInfo=new ApiInfo();
        apiInfo.setPath("/save");
        apiInfo.setName("save-"+tableName);
        apiInfo.setGroupId(groupId);
        apiInfo.setMethod(HttpMethod.POST.name());
        //requestBodyDefinition
        apiInfo.setRequestBodyDefinition(buildBaseDefinition());
        //requestBody
        apiInfo.setRequestBody(buildRequestBodyJson().toJSONString());
        //script
        apiInfo.setScript(generateSaveScript());
        //保存
        AssertUtil.isTrue(MagicConfiguration.getMagicResourceService().saveFile(apiInfo),
                new MagicApiCurdGenerateException(String.format("generate save api fail,apiInfo.name = %s",apiInfo.getName())));
    }

    /**
     * 生成 删除API
     */
    private void generateDeleteApi(){
        ApiInfo apiInfo=new ApiInfo();
        apiInfo.setName("delete-"+tableName);
        apiInfo.setPath("/delete");
        apiInfo.setGroupId(groupId);
        apiInfo.setMethod(HttpMethod.POST.name());
        //parameters
        List<Parameter> parameterList=new ArrayList<>();
        for (TableColumn tableColumn : tableColumns) {
            if(!tableColumn.getPrimary()){
                log.debug("skip no primary column,column.name= {},tableName= {}",tableColumn.getColumnName(),tableName);
                continue;
            }
            Parameter parameter=new Parameter();
            parameter.setName(tableColumn.getColumnName());
            parameter.setDescription(tableColumn.getColumnComment());
            parameterList.add(parameter);
        }
        apiInfo.setParameters(parameterList);
        //script
        apiInfo.setScript(generateDeleteScript());
        //保存
        AssertUtil.isTrue(MagicConfiguration.getMagicResourceService().saveFile(apiInfo),
                new MagicApiCurdGenerateException(String.format("generate delete api fail,apiInfo.name = %s",apiInfo.getName())));
    }

    /**
     * 构建 CURD API 的请求实体 JSON
     */
    private JSONObject buildRequestBodyJson() {
        JSONObject requestBodyJson=new JSONObject();
        for (TableColumn tableColumn : tableColumns) {
            requestBodyJson.put(lineToHump(tableColumn.getColumnName()),"");
        }
        return requestBodyJson;
    }

    /**
     * 构建 CURD API 的 {@link BaseDefinition}
     */
    private BaseDefinition buildBaseDefinition() {
        BaseDefinition baseDefinition=new BaseDefinition();
        baseDefinition.setName("");
        baseDefinition.setValue("");
        baseDefinition.setDescription("");
        baseDefinition.setRequired(false);
        baseDefinition.setDataType(DataType.Object);
        baseDefinition.setValidateType("");
        baseDefinition.setError("");
        baseDefinition.setExpression("");
        ArrayList<BaseDefinition> columnDefinitionList=new ArrayList<>();
        for (TableColumn tableColumn : tableColumns) {
            BaseDefinition columnDefinition=new BaseDefinition();
            columnDefinition.setName(lineToHump(tableColumn.getColumnName()));
            columnDefinition.setValue("");
            columnDefinition.setDescription(tableColumn.getColumnComment());
            columnDefinition.setDataType(DataType.String);
            columnDefinition.setValidateType("");
            columnDefinition.setError("");
            columnDefinition.setExpression("");
            columnDefinitionList.add(columnDefinition);
        }
        baseDefinition.setChildren(columnDefinitionList);
        return baseDefinition;
    }

    /**
     * 生成 删除API 脚本
     */
    private String generateDeleteScript(){
        //查询条件
        int tableColumnSize = tableColumns.size();
        StringBuilder queryCondition =new StringBuilder();
        for (int i = 0; i < tableColumnSize; i++) {
            TableColumn tableColumn = tableColumns.get(i);
            if(!tableColumn.getPrimary()){
                log.debug("skip no primary column,column.name= {},tableName= {}",tableColumn.getColumnName(),tableName);
                continue;
            }
            String columnName = tableColumn.getColumnName();
            queryCondition
                    .append("  .eq(\"").append(columnName).append("\",")
                    .append(lineToHump(columnName)).append(")");
            if(i!= tableColumnSize -1){
                queryCondition.append("\r\n");
            }
        }
        //覆盖占位符，返回脚本内容
        return getScriptTemplate(DELETE_SCRIPT_TEMPLATE)
                .replace("#{dataSourceKey}", StringUtils.isNotEmpty(dataSourceInfo.getKey())?"."+dataSourceInfo.getKey():"")
                .replace("#{tableName}",tableName)
                .replace("#{queryCondition}",queryCondition);
    }

    /**
     * 生成 保存Api 脚本
     */
    private String generateSaveScript(){
        //主键列
        String primaryColumn = tableColumns.stream()
                .filter(TableColumn::getPrimary)
                .map(TableColumn::getColumnName)
                .collect(Collectors.joining(","));
        //请求实体
        int tableColumnSize = tableColumns.size();
        StringBuilder requestBody=new StringBuilder();
        for (int i = 0; i < tableColumns.size(); i++) {
            TableColumn tableColumn = tableColumns.get(i);
            String columnName = tableColumn.getColumnName();
            requestBody.append(columnName).append(": body.").append(lineToHump(columnName));
            if(i!= tableColumnSize -1){
                requestBody.append(",\r\n");
            }
        }
        //覆盖占位符，返回脚本内容
        return getScriptTemplate(SAVE_SCRIPT_TEMPLATE)
                .replace("#{dataSourceKey}", StringUtils.isNotEmpty(dataSourceInfo.getKey())?"."+dataSourceInfo.getKey():"")
                .replace("#{tableName}",tableName)
                .replace("#{primaryColumn}",primaryColumn)
                .replace("#{requestBody}",requestBody);
    }

    /**
     * 生成 分页Api 脚本
     */
    private String generatePageScript(){
        //列名
        String columnsStr =tableColumns.stream()
                .map(TableColumn::getColumnName)
                .collect(Collectors.joining(","));
        //查询条件
        int tableColumnSize = tableColumns.size();
        StringBuilder queryCondition =new StringBuilder();
        for (int i = 0; i < tableColumnSize; i++) {
            TableColumn tableColumn = tableColumns.get(i);
            String columnName = tableColumn.getColumnName();
            String humpColumnName = lineToHump(columnName);
            queryCondition.append(" ?{ body.")
                    .append(humpColumnName).append(", and ")
                    .append(columnName).append(" = #{body.")
                    .append(humpColumnName).append("} }");
            if(i!= tableColumnSize -1){
                queryCondition.append("\r\n");
            }
        }
        //覆盖占位符，返回脚本内容
        return getScriptTemplate(PAGE_SCRIPT_TEMPLATE)
                .replace("#{dataSourceKey}", StringUtils.isNotEmpty(dataSourceInfo.getKey())?"."+dataSourceInfo.getKey():"")
                .replace("#{tableName}", tableName)
                .replace("#{tableColumns}", columnsStr)
                .replace("#{queryCondition}",queryCondition);
    }

    /**
     * 获取脚本模板内容
     * @param scriptTemplateFileName 脚本模板文件名
     */
    private String getScriptTemplate(String scriptTemplateFileName){
        try {
            InputStream scriptTemplateIs = getClass().getClassLoader().getResourceAsStream(scriptTemplateFileName);
            if(scriptTemplateIs==null){
                throw new MagicApiCurdGenerateException("not found "+scriptTemplateFileName+" from resource");
            }
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(scriptTemplateIs,
                            StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            String s;
            while ((s=reader.readLine())!=null) {
                sb.append(s).append("\r\n");
            }
            return sb.toString();
        } catch (IOException e) {
            throw new MagicApiCurdGenerateException("read "+scriptTemplateFileName+" content fail",e);
        }
    }

    /**
     * 下划线转驼峰
     * @param str 要转换的字符串
     */
    private String lineToHump(String str) {
        Matcher matcher = LINE_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}
